#include "mboard.h"
#include "mboard_private.h"
#include "usart_hw.h"
#include "gpio_hw.h"
#include "dma_hw.h"
#include "rcc.h"
#include "nvic.h"
#include "mdebug.h"
#include "mgdb.h"
#include <string.h>

volatile uint32_t tmp1[5];
/**
 * USART1连接到了CH340，用作串口打印
 *    PA9 --- USART1_TX
 *    PA10 -- USART1_RX
 *    DMA2 -- 数据流7 -- 通道4 -- USART1_TX
 *    DMA2 -- 数据流5 -- 通道4 -- USART1_RX
 */

typedef struct MDmaUsartTx {
	MDma_t dma;
	uint8_t dat[64];
} MDmaUsartTx_t;

typedef struct MDmaUsartRx {
	MDma_t dma;
	uint8_t dat[16];
} MDmaUsartRx_t;

static MDmaUsartTx_t mdmaUsartTx[8];
static MDmaUsartTx_t *mdmaUsartTxAct;
static MDmaUsartRx_t mdmaUsartRx[16];
static MDmaUsartRx_t *mdmaUsartRxAct;
static const uint8_t *mdmaUsartRxPtr;


static void mUsartClockInit(void)
{
	/* 使能USART1时钟 */
	RCC_EnableApb2(RCC_CLK_APB2_USART1);
	RCC_EnableAhb1(RCC_CLK_AHB1_GPIOA);
}

static void mUsartGpioInit(void)
{
	uint32_t tmp32;

	/* 复用模式 */
	tmp32 = SOC_GPIOA.MODER;
	tmp32 &= ~GPIO_MODER_MASK(9);
	tmp32 &= ~GPIO_MODER_MASK(10);
	tmp32 |= GPIO_MODER_AF(9);
	tmp32 |= GPIO_MODER_AF(10);
	SOC_GPIOA.MODER = tmp32;

	/* PA9_TX推挽输出 */
	tmp32 = SOC_GPIOA.OTYPER;
	tmp32 &= ~GPIO_OTYPER_MASK(9);
	tmp32 |= GPIO_OTYPER_PP(9);
	SOC_GPIOA.OTYPER = tmp32;

	/* PA9_TX 25M的输出速度 */
	tmp32 = SOC_GPIOA.OSPEEDR;
	tmp32 &= ~GPIO_OSPEEDR_MASK(9);
	tmp32 |= GPIO_OSPEEDR_25M(9);
	SOC_GPIOA.OSPEEDR = tmp32;

	/* PA9_TX禁用上下拉, PA10_RX启用上拉 */
	tmp32 = SOC_GPIOA.PUPDR;
	tmp32 &= ~GPIO_PUPDR_MASK(9);
	tmp32 &= ~GPIO_PUPDR_MASK(10);
	tmp32 |= GPIO_PUPDR_NONE(9);
	tmp32 |= GPIO_PUPDR_UP(10);
	SOC_GPIOA.PUPDR = tmp32;

	/* IO复用为USART1 */
	tmp32 = SOC_GPIOA.AFHR;
	tmp32 &= ~GPIO_AFHR_MASK(9);
	tmp32 &= ~GPIO_AFHR_MASK(10);
	tmp32 |= GPIO_AFHR_0_15D(9, 7);
	tmp32 |= GPIO_AFHR_0_15D(10, 7);
	SOC_GPIOA.AFHR = tmp32;
}

static void mUsartDeviceInit(uint32_t baud)
{
	uint32_t brr;
	uint32_t over8;

	SOC_USART1.CR1 = USART_CR1_SBK_DIS |     // 不发送断路字符
	                 USART_CR1_RWU_ACTIVE |  // 接收器处于活动模式
	                 USART_CR1_RE_DIS |      // 禁止接收器
	                 USART_CR1_TE_DIS |      // 禁止发送器
	                 USART_CR1_IDLEIE_EN |   // 使能线路空闲中断，当串口接收
	                                         // 到1个字节后，如果第二个字节没
	                                         // 有连续发送过来，就会产生一次
	                                         // IDLE事件
	                 USART_CR1_RXNEIE_EN  |  // 使能接收中断，使用DMA时应
	                                         // 禁止该中断
	                 USART_CR1_TCIE_DIS |    // 禁止发送完成中断
	                 USART_CR1_TXEIE_DIS |   // 禁止发送为空中断
	                 USART_CR1_PEIE_EN |     // 使能奇偶校验错误中断
	                 USART_CR1_PS_NONE |     // 不校验
	                 USART_CR1_WAKE_IDLE |   // 通过空闲线路唤醒
	                 USART_CR1_M_8BIT |      // 数据位宽8bit
	                 USART_CR1_UE_EN |       // 使能预分频器
	                 USART_CR1_OVER_16;      // 16倍采样
	SOC_USART1.CR2 = USART_CR2_ADD_0_FH(0) | // 多处理器通信时使用该域
	                 USART_CR2_LBDL_10BIT |  // LIN 10位断路检测
	                 USART_CR2_LBDIE_DIS |   // 禁止LIN断路检测中断
	                 USART_CR2_LBCL_DIS |    // 同步模式下，最后一个位的时钟
	                                         // 在SCLK上输出，同步模式不适用
	                                         // UART4/5
	                 USART_CR2_CPHA_FIRST |  // 在时钟变化的第一个时钟沿捕获
	                                        //  数据
	                 USART_CR2_CPOL_LOW |    // SCLK空闲时为低电平
	                 USART_CR2_CLKEN_DIS |   // 启用SCLK
	                 USART_CR2_STOP_1 |      // 1个停止位
	                 USART_CR2_LINEN_DIS;    // 禁用LIN功能
	SOC_USART1.CR3 = USART_CR3_EIE_EN |      // 使能DMA错误中断
	                 USART_CR3_IREN_DIS |    // 禁止IrDA模式
	                 USART_CR3_IRLP_DIS |    // IrDA正常模式
	                 USART_CR3_HDSEL_DIS |   // 禁止半双工模式
	                 USART_CR3_NACK_DIS |    // 智能卡出现奇偶校验错误时不发
	                                         // 送NACK，不适用UART4/5
	                 USART_CR3_SCEN_DIS |    // 禁用智能卡模式
	                 USART_CR3_DMAR_DIS |    // 禁止DMA接收，需要等到DMA使能
	                 USART_CR3_DMAT_DIS |    // 禁止DMA发送，需要等到DMA使能
	                                         // 后才能使能，否者会出现FIFO错误
	                 USART_CR3_RTSE_DIS |    // 禁止RTS（数据流控）
	                 USART_CR3_CTSE_DIS |    // 禁止CTS（数据流控）
	                 USART_CR3_CTSIE_DIS |   // 禁止CTS中断
	                 USART_CR3_ONEBIT_DIS;   // 3次采样，可检测线路噪声

	/* USART1在APB2上，时钟频率最大84M，乘2是为了四舍五入 */
	brr = mboardInfo.clk.apb2 * 2;

	/* 仅UART模式支持8倍采样率，相同APB时钟下，使用8倍采样率可以提高波特
	   率的上限 */
	over8 = SOC_USART1.CR1 & USART_CR1_OVER_8;

	/* 计算带小数的分频值，频率应当先除以采样率，再除以波特率，此处没有除以
	   采样率，相当于放大了8或16倍，整数和小数也放大了8或16倍 */
	brr /= baud;
	/* 将整数和小数缩小2倍并四舍五入 */
	brr = (brr >> 1) + (brr & 0x1U);

	if (over8) {
		/* 当采样率为8的时候，bit4强制为0 */
		brr = ((brr >> 4) << 4) + (brr & 0x7U);
	}

	/* 整数部分[15:4]对应为[11:0]，相当于缩小16倍，小数部分[3:0]除以16或8
	   即可得到小数 */
	SOC_USART1.BRR = brr;

	/* 使能UART TX/RX */
	SOC_USART1.CR1 |= USART_CR1_RE_EN | USART_CR1_TE_EN;
}

static void mUsartInterrupt(void)
{
	NVIC_SetPriority(NVIC_IRQ_USART1, NVIC_PRIORITY_LOW,
	                 NVIC_PRIORITY_SUB_NONE);
	NVIC_EnableIrq(NVIC_IRQ_USART1);
}

static void mUsartDmaInit(void)
{
	uint32_t i;

	mdmaUsartTxAct = mdmaUsartTx;
	mdmaUsartRxAct = mdmaUsartRx;
	mdmaUsartRxPtr = mdmaUsartRx[0].dat;

	/* 发送缓冲区默认全部是CPU访问权限，接收缓存区全部标记位DMA访问权限 */
	for (i = 0; i < sizeof(mdmaUsartRx)/sizeof(MDmaUsartRx_t); i++) {
		mdmaUsartRx[i].dma.flag = MDMA_FLAG_DMA;
	}
}

void mUsartInit(void)
{
	mUsartClockInit();
	mUsartGpioInit();
	mUsartDeviceInit(115200);
	mUsartDmaInit();
	mUsartInterrupt();
}

/**
 * 计算环形MDmaUsartTx_t
 */
static MDmaUsartTx_t *mUsartTxDmaActNext(MDmaUsartTx_t *dmaAct)
{
	dmaAct++;
	/* 判断如果是超出了范围则回到起始位置 */
	if (dmaAct == &mdmaUsartTx[sizeof(mdmaUsartTx)/sizeof(MDmaUsartTx_t)]) {
		return mdmaUsartTx;
	} else {
		return dmaAct;
	}
}

/**
 * CPU获取当前记录的访问权限
 */
static MDmaUsartTx_t *mUsartTxGetDmaAct(void)
{
	/* 此处需要屏蔽DMA的中断 */
	NVIC_DisableIrq(NVIC_IRQ_DMA2_STREAM7);
	/* 判断记录是否正在发送，或记录是否已满 */
	if ((mdmaUsartTxAct->dma.flag == MDMA_FLAG_DMA_ACT) ||
	    (mdmaUsartTxAct->dma.ndtr == sizeof(mdmaUsartTxAct->dat))) {
		/* CPU激活下一个记录 */
		mdmaUsartTxAct = mUsartTxDmaActNext(mdmaUsartTxAct);
		NVIC_EnableIrq(NVIC_IRQ_DMA2_STREAM7);
		/* 如果下一条记录不属于CPU可访问，则等待 */
		while (mdmaUsartTxAct->dma.flag != MDMA_FLAG_CPU) {
		}
	} else {
		mdmaUsartTxAct->dma.flag = MDMA_FLAG_CPU;
		NVIC_EnableIrq(NVIC_IRQ_DMA2_STREAM7);
	}
	return mdmaUsartTxAct;
}

/**
 * 调用该函数之后CPU不能再使用mdmaUsartTxAct，需调用mUsartTxGetDmaAct()
 * 重新获取权限
 */
static void mUsartTxDmaStart(void)
{
	/* 判断DMA是否空闲 */
	if (_DMA_IsBusy(SOC_DMA2_S7)) {
		/* 将mdmaUsartTxAct记录权限交给DMA，等待执行 */
		mdmaUsartTxAct->dma.flag = MDMA_FLAG_DMA;
		return;
	} else {
		/* 将mdmaUsartTxAct记录权限交给DMA，立即执行 */
		mdmaUsartTxAct->dma.flag = MDMA_FLAG_DMA_ACT;
	}

	/* 配置DMA FIFO */
	SOC_DMA2_S7.FCR = DMA_FCR_FTH_1 |       // 1/4容量，直接模式下FIFO不可用
	                  DMA_FCR_DMDIS_EN |    // 使能直接模式
	                  DMA_FCR_FEIE_EN;      // 使能FIFO错误中断
	/* 外设地址 */
	SOC_DMA2_S7.PAR = DMA_PAR_0_FFFFFFFFH(&SOC_USART1.DR);
	/* 存储器地址 */
	SOC_DMA2_S7.M0AR = DMA_M0AR_0_FFFFFFFFH(mdmaUsartTxAct->dat);
	/* 顺序传输，大小为要发送的字节数 */
	SOC_DMA2_S7.NDTR = DMA_NDTR_0_65535D(mdmaUsartTxAct->dma.ndtr);
	/* 清除所有状态标志 */
	SOC_DMA2.IFCR[DMA_IDX_4_7D(7)] = DMA_S7_FLAG(DMA_FLAG_ALL);
	/* 配置DMA */
	SOC_DMA2_S7.CR = DMA_CR_EN |            // 启动传输
	                 DMA_CR_DMEIE_EN |      // 使能直接模式错误中断
	                 DMA_CR_TEIE_EN |       // 使能传输错误中断
	                 DMA_CR_HTIE_DIS |      // 禁止半完成中断
	                 DMA_CR_TCIE_EN |       // 使能传输完成中断
	                 DMA_CR_PFCTRL_DMA |    // 由DMA控制传输完成
	                 DMA_CR_DIR_M2P |       // 存储到外设
	                 DMA_CR_CIRC_DIS |      // 禁用循环模式
	                 DMA_CR_PINC_DIS |      // 外设地址固定
	                 DMA_CR_MINC_EN |       // 存储地址递增
	                 DMA_CR_PSIZE_1 |       // 外设地址加1，PINC_DIS时忽略
	                 DMA_CR_MSIZE_1 |       // 存储器地址加1
	                 DMA_CR_PINCOS_DIS |    // 外设递增由PSIZE决定，PINC=0时
	                 DMA_CR_PL_LOW |        // 最低优先级
	                 DMA_CR_DBM_DIS |       // 禁用双缓冲区模式
	                 DMA_CR_CT_M0 |         // 当前使用M0AR寻址
	                 DMA_CR_PBURST_SINGLE | // 单次传输
	                 DMA_CR_MBURST_SINGLE | // 单次传输
	                 DMA_CR_CHSEL_4;        // 选择通道4
	/* 使能USART TX DMA请求，需要先使能DMA才能使能TX请求,否者DMA会置
	   起FEIF标志，因为TX为空的时候会请求DMA发数据，但是DMA尚未就绪 */
	SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
	                  SOC_BIT_USART_CR3_DMAT, SOC_VAL_USART_CR3_DMAT_EN);
}

/**
 * USART1 TX DMA完成中断服务函数
 */
void mUsartTxDmaTCI(void)
{
	/* 根据存储地址，计算出MDmaUsartTx_t结构体 */
	MDmaUsartTx_t *dmaAct = (MDmaUsartTx_t *)
	                        (SOC_DMA2_S7.M0AR - sizeof(MDma_t));

	/* 当前记录已发送完毕，长度标记为0，标记为CPU可访问 */
	dmaAct->dma.ndtr = 0;
	dmaAct->dma.flag = MDMA_FLAG_CPU;
	/* DMA激活下一条记录 */
	dmaAct = mUsartTxDmaActNext(dmaAct);
	if (dmaAct->dma.flag == MDMA_FLAG_CPU) {
		/* 如果下一条记录是CPU可访问的，则关闭USART TX DMA请求 */
		SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
		                  SOC_BIT_USART_CR3_DMAT, SOC_VAL_USART_CR3_DMAT_DIS);
		/* 关闭DMA */
		SOC_DMA2_S7.CR = DMA_CR_DIS;
		/* 写0清除发送完成的状态标志，以便于mUsartTxWaitFinish()判断串口
		   发送完成 */
		SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_SR,
		                  SOC_BIT_USART_SR_TC, SOC_VAL_USART_SR_TC_CLEAR);
	} else {
		/* 标记为正在执行 */
		dmaAct->dma.flag = MDMA_FLAG_DMA_ACT;
		/* 如果下一条记录是DMA可访问的，则继续搬运 */
		/* 存储器地址 */
		SOC_DMA2_S7.M0AR = DMA_M0AR_0_FFFFFFFFH(dmaAct->dat);
		/* 顺序传输 */
		SOC_DMA2_S7.NDTR = DMA_NDTR_0_65535D(dmaAct->dma.ndtr);
		/* 清除所有状态标志 */
		SOC_DMA2.IFCR[DMA_IDX_4_7D(7)] = DMA_S7_FLAG(DMA_FLAG_ALL);
		/* 配置DMA */
		SOC_DMA2_S7.CR = DMA_CR_EN |            // 启动传输
		                 DMA_CR_DMEIE_EN |      // 使能直接模式错误中断
		                 DMA_CR_TEIE_EN |       // 使能传输错误中断
		                 DMA_CR_HTIE_DIS |      // 禁止半完成中断
		                 DMA_CR_TCIE_EN |       // 使能传输完成中断
		                 DMA_CR_PFCTRL_DMA |    // 由DMA控制传输完成
		                 DMA_CR_DIR_M2P |       // 存储到外设
		                 DMA_CR_CIRC_DIS |      // 禁用循环模式
		                 DMA_CR_PINC_DIS |      // 外设地址固定
		                 DMA_CR_MINC_EN |       // 存储地址递增
		                 DMA_CR_PSIZE_1 |       // 外设地址加1，PINC_DIS时忽略
		                 DMA_CR_MSIZE_1 |       // 存储器地址加1
		                 DMA_CR_PINCOS_DIS |    // 外设递增由PSIZE决定，
		                                        // PINC=0时
		                 DMA_CR_PL_LOW |        // 最低优先级
		                 DMA_CR_DBM_DIS |       // 禁用双缓冲区模式
		                 DMA_CR_CT_M0 |         // 当前使用M0AR寻址
		                 DMA_CR_PBURST_SINGLE | // 单次传输
		                 DMA_CR_MBURST_SINGLE | // 单次传输
		                 DMA_CR_CHSEL_4;        // 选择通道4
	}
}

/**
 * 计算环形MDmaUsartRx_t
 */
static MDmaUsartRx_t *mUsartRxDmaActNext(MDmaUsartRx_t *dmaAct)
{
	dmaAct++;
	/* 判断如果是超出了范围则回到起始位置 */
	if (dmaAct == &mdmaUsartRx[sizeof(mdmaUsartRx)/sizeof(MDmaUsartRx_t)]) {
		return mdmaUsartRx;
	} else {
		return dmaAct;
	}
}

/**
 * CPU获取有效记录
 */
static uint16_t mUsartRxGetDmaAct(void)
{
	/* 当前记录还有剩余数据 */
	if (mdmaUsartRxAct->dma.flag == MDMA_FLAG_CPU) {
		/* 当前记录中剩余数据为0 */
		if (mdmaUsartRxAct->dma.ndtr == 0) {
			/* 修改为DMA权限 */
			mdmaUsartRxAct->dma.flag = MDMA_FLAG_DMA;
			/* CPU尝试获取下一条记录 */
			mdmaUsartRxAct = mUsartRxDmaActNext(mdmaUsartRxAct);
			mdmaUsartRxPtr = mdmaUsartRxAct->dat;
			/* 新记录如果是CPU可访问，则表示还有数据可读取 */
			if (mdmaUsartRxAct->dma.flag == MDMA_FLAG_CPU) {
				/* 返回新记录中可用数据长度 */
				return mdmaUsartRxAct->dma.ndtr;
			}
		} else {
			/* 返回当前记录剩余数据长度 */
			return mdmaUsartRxAct->dma.ndtr;
		}
	}

	/* 该标志在USART IDLE中断中修改mUsartRxIdle()  */
	if (mdmaUsartRxAct->dma.flag == MDMA_FLAG_DMA) {
		/* 新记录中无数据，直接返回 */
		return 0;
	}

	/* 新记录中有数据，从新记录中读取，关闭USART RX DMA请求 */
	SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
	                  SOC_BIT_USART_CR3_DMAR, SOC_VAL_USART_CR3_DMAR_DIS);
	/* 关闭DMA传输 */
	SOC_DMA2_S5.CR = DMA_CR_DIS;
	/* 等待DMA结束 */
	while (_DMA_IsBusy(SOC_DMA2_S5));
	/* 记录DMA已搬运的项目数 */
	uint32_t ndtr = SOC_DMA2_S5.NDTR;
	/* 做一次伪DMA完成动作 */
	mUsartRxDmaTCI();
	/* 如果DMA正常使能，则开启RX DMA请求 */
	if (_DMA_IsBusy(SOC_DMA2_S5)) {
		SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
		                  SOC_BIT_USART_CR3_DMAR, SOC_VAL_USART_CR3_DMAR_EN);
	}
	/* 计算有效数据长度 */
	mdmaUsartRxAct->dma.ndtr = sizeof(mdmaUsartRxAct->dat) - ndtr;
	/* 更新有效数据的位置 */
	mdmaUsartRxPtr = mdmaUsartRxAct->dat;
	/* 返回可用数据长度 */
	return mdmaUsartRxAct->dma.ndtr;
}

/**
 * 该函数在RX中断中调用
 */
void mUsartRxDmaStart(void)
{
	/* 判断DMA是否空闲 */
	if (_DMA_IsBusy(SOC_DMA2_S5)) {
		return;
	}

	/* 配置DMA FIFO */
	SOC_DMA2_S5.FCR = DMA_FCR_FTH_1 |       // 1/4容量，直接模式下FIFO不可用
	                  DMA_FCR_DMDIS_EN |    // 使能直接模式
	                  DMA_FCR_FEIE_EN;      // 使能FIFO错误中断
	/* 外设地址 */
	SOC_DMA2_S5.PAR = DMA_PAR_0_FFFFFFFFH(&SOC_USART1.DR);
	/* 存储器地址 */
	SOC_DMA2_S5.M0AR = DMA_M0AR_0_FFFFFFFFH(mdmaUsartRxAct->dat);
	/* 顺序传输，大小为dmaAct->dat大小 */
	SOC_DMA2_S5.NDTR = DMA_NDTR_0_65535D(sizeof(mdmaUsartRxAct->dat));
	/* 清除所有状态标志 */
	SOC_DMA2.IFCR[DMA_IDX_4_7D(5)] = DMA_S5_FLAG(DMA_FLAG_ALL);
	/* 配置DMA */
	SOC_DMA2_S5.CR = DMA_CR_EN |            // 启动传输
	                 DMA_CR_DMEIE_EN |      // 使能直接模式错误中断
	                 DMA_CR_TEIE_EN |       // 使能传输错误中断
	                 DMA_CR_HTIE_DIS |      // 禁止半完成中断
	                 DMA_CR_TCIE_EN |       // 使能传输完成中断
	                 DMA_CR_PFCTRL_DMA |    // 由DMA控制传输完成
	                 DMA_CR_DIR_P2M |       // 外设到存储
	                 DMA_CR_CIRC_DIS |      // 禁用循环模式
	                 DMA_CR_PINC_DIS |      // 外设地址固定
	                 DMA_CR_MINC_EN |       // 存储地址递增
	                 DMA_CR_PSIZE_1 |       // 外设地址加1，PINC_DIS时忽略
	                 DMA_CR_MSIZE_1 |       // 存储器地址加1
	                 DMA_CR_PINCOS_DIS |    // 外设递增由PSIZE决定，PINC=0时
	                 DMA_CR_PL_LOW |        // 最低优先级
	                 DMA_CR_DBM_DIS |       // 禁用双缓冲区模式
	                 DMA_CR_CT_M0 |         // 当前使用M0AR寻址
	                 DMA_CR_PBURST_SINGLE | // 单次传输
	                 DMA_CR_MBURST_SINGLE | // 单次传输
	                 DMA_CR_CHSEL_4;        // 选择通道4
	/* 使能USART RX DMA请求，需要先使能DMA才能使能RX请求,否者DMA可能会置
	   起FEIF标志，因为RX非空的时候会请求DMA接收数据，但是DMA尚未就绪 */
	SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
	                  SOC_BIT_USART_CR3_DMAR, SOC_VAL_USART_CR3_DMAR_EN);
}

/**
 * USART RX DMA接收完成中断服务函数
 */
void mUsartRxDmaTCI(void)
{
	/* 根据存储地址，计算出MDmaUsartRx_t结构体 */
	MDmaUsartRx_t *dmaAct = (MDmaUsartRx_t *)
	                        (SOC_DMA2_S5.M0AR - sizeof(MDma_t));

	/* 当前记录已接收完毕，标记为CPU可访问 */
	dmaAct->dma.flag = MDMA_FLAG_CPU;
	/* 数据长度 */
	dmaAct->dma.ndtr = sizeof(dmaAct->dat);
	/* DMA激活下一条记录 */
	dmaAct = mUsartRxDmaActNext(dmaAct);
	if (dmaAct->dma.flag == MDMA_FLAG_CPU) {
		/* 如果下一条记录是CPU可访问的，则表示空间不足，关闭RX DMA请求 */
		SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
		                  SOC_BIT_USART_CR3_DMAR, SOC_VAL_USART_CR3_DMAR_DIS);
		/* 关闭DMA */
		SOC_DMA2_S5.CR = DMA_CR_DIS;
		/* 使能RX中断，如果有新数据来会触发中断，进而启用DMA接收数据 */
		SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR1,
		          SOC_BIT_USART_CR1_RXNEIE, SOC_VAL_USART_CR1_RXNEIE_EN);
	} else {
		/* 如果下一条记录是DMA可访问的，则继续搬运 */
		/* 存储器地址 */
		SOC_DMA2_S5.M0AR = DMA_M0AR_0_FFFFFFFFH(dmaAct->dat);
		/* 顺序传输 */
		SOC_DMA2_S5.NDTR = DMA_NDTR_0_65535D(sizeof(dmaAct->dat));
		/* 清除所有状态标志 */
		SOC_DMA2.IFCR[DMA_IDX_4_7D(5)] = DMA_S5_FLAG(DMA_FLAG_ALL);
		/* 配置DMA */
		SOC_DMA2_S5.CR = DMA_CR_EN |            // 启动传输
		                 DMA_CR_DMEIE_EN |      // 使能直接模式错误中断
		                 DMA_CR_TEIE_EN |       // 使能传输错误中断
		                 DMA_CR_HTIE_DIS |      // 禁止半完成中断
		                 DMA_CR_TCIE_EN |       // 使能传输完成中断
		                 DMA_CR_PFCTRL_DMA |    // 由DMA控制传输完成
		                 DMA_CR_DIR_P2M |       // 外设到存储
		                 DMA_CR_CIRC_DIS |      // 禁用循环模式
		                 DMA_CR_PINC_DIS |      // 外设地址固定
		                 DMA_CR_MINC_EN |       // 存储地址递增
		                 DMA_CR_PSIZE_1 |       // 外设地址加1，PINC_DIS时忽略
		                 DMA_CR_MSIZE_1 |       // 存储器地址加1
		                 DMA_CR_PINCOS_DIS |    // 外设递增由PSIZE决定，
		                                        // PINC=0时
		                 DMA_CR_PL_LOW |        // 最低优先级
		                 DMA_CR_DBM_DIS |       // 禁用双缓冲区模式
		                 DMA_CR_CT_M0 |         // 当前使用M0AR寻址
		                 DMA_CR_PBURST_SINGLE | // 单次传输
		                 DMA_CR_MBURST_SINGLE | // 单次传输
		                 DMA_CR_CHSEL_4;        // 选择通道4
	}
}

/**
 * USART IDLE中断服务函数
 */
void mUsartRxIdle(void)
{
	/* 根据存储地址，计算出MDmaUsartRx_t结构体 */
	MDmaUsartRx_t *dmaAct = (MDmaUsartRx_t *)
	                        (SOC_DMA2_S5.M0AR - sizeof(MDma_t));

	/* 有数据已接收 */
	dmaAct->dma.flag = MDMA_FLAG_DMA_ACT;
	/* 伪读用于清除中断标志 */
	SOC_USART1.DR;
}

#if 0
void _mUsartTxByte(uint8_t ch)
{
	while (!(SOC_USART1.SR & USART_SR_TXE));
	SOC_USART1.DR = ch;
}

void _mUsartTxBytes(const uint8_t *bytes, uint32_t len)
{
	const uint8_t *p = bytes + len;

	while (bytes != p) {
		while (!(SOC_USART1.SR & USART_SR_TXE));
		SOC_USART1.DR = *bytes++;
	}
}

void _mUsartTxString(const char *str)
{
	while (*str) {
		while (!(SOC_USART1.SR & USART_SR_TXE));
		SOC_USART1.DR = *str++;
	}
}

uint8_t _mUsartRxByte(void)
{
	while (!(SOC_USART1.SR & USART_SR_RXNE));
	return (uint8_t)SOC_USART1.DR;
}

uint32_t _mUsartRxBytes(uint8_t *bytes, uint32_t maxLen)
{
	if (maxLen) {
		if (SOC_USART1.SR & USART_SR_RXNE) {
			*bytes++ = (uint8_t)SOC_USART1.DR;
			return 1;
		}
	}

	return 0;
}

void _mUsartTxWaitFinish(void)
{
	while (!(SOC_USART1.SR & USART_SR_TC));
}

void _mUsartRxClear(void)
{
}
#else
void mUsartTxByte(uint8_t ch)
{
	MDmaUsartTx_t *dmaAct = mUsartTxGetDmaAct();

	/* 记录输出字符 */
	dmaAct->dat[dmaAct->dma.ndtr] = ch;
	/* 记录输出长度 */
	dmaAct->dma.ndtr++;
	/* 尝试启动DMA传输，如果DMA正忙，则直接返回 */
	mUsartTxDmaStart();
}

void mUsartTxBytes(const uint8_t *bytes, uint32_t len)
{
	uint32_t tmp1;
	MDmaUsartTx_t *dmaAct;

	if (len == 0) {
		return;
	}

	while (1) {
		/* 获取可用记录 */
		dmaAct = mUsartTxGetDmaAct();
		/* 计算当前记录可用空间 */
		tmp1 = sizeof(dmaAct->dat) - dmaAct->dma.ndtr;
		if (tmp1 >= len) {
			/* 当前记录空间充足，直接拷贝 */
			memcpy(&dmaAct->dat[dmaAct->dma.ndtr], bytes, len);
			dmaAct->dma.ndtr += len;
			/* 尝试启动DMA传输，如果DMA正忙，则直接返回 */
			mUsartTxDmaStart();
			return;
		} else {
			/* 当前记录空间不足，先拷贝一部分 */
			memcpy(&dmaAct->dat[dmaAct->dma.ndtr], bytes, tmp1);
			dmaAct->dma.ndtr = sizeof(dmaAct->dat);
			/* 尝试启动DMA传输，如果DMA正忙，则直接返回 */
			mUsartTxDmaStart();
			len -= tmp1;
			bytes += tmp1;
		}
	}
}

void mUsartTxString(const char *str)
{
	uint32_t len = 0;

	while (str[len]) {
		len++;
	}

	mUsartTxBytes((const uint8_t *)str, len);
}

uint8_t mUsartRxByte(void)
{
	/* 检查是否有接收的数据 */
	while (mUsartRxGetDmaAct() == 0);
	mdmaUsartRxAct->dma.ndtr--;
	return *mdmaUsartRxPtr++;
}

uint32_t mUsartRxBytes(uint8_t *bytes, uint32_t maxLen)
{
	uint32_t len;
	uint8_t *start = bytes;

	while (1) {
		len = mUsartRxGetDmaAct();
		if (len == 0) {
			return (uint32_t)(bytes-start);
		}
		if (len >= maxLen) {
			memcpy(bytes, mdmaUsartRxPtr, maxLen);
			mdmaUsartRxAct->dma.ndtr -= maxLen;
			mdmaUsartRxPtr += maxLen;
			return (uint32_t)(bytes-start) + maxLen;
		} else {
			memcpy(bytes, mdmaUsartRxPtr, len);
			mdmaUsartRxAct->dma.ndtr = 0;
			maxLen -= len;
			bytes += len;
		}
	}
}
void mUsartTxWaitFinish(void)
{
	/* DMA传输关闭时即数据已搬运完毕 */
	while (SOC_BITBAND_READ(SOC_BASE_USART1, SOC_OFF_USART_CR3,
	       SOC_BIT_USART_CR3_DMAT) != SOC_VAL_USART_CR3_DMAT_DIS);
	/* 等待最后一个字节的数据发送完成 */
	while (SOC_BITBAND_READ(SOC_BASE_USART1, SOC_OFF_USART_SR,
	       SOC_BIT_USART_SR_TC) != SOC_VAL_USART_SR_TC_SET);
}

void mUsartRxClear(void)
{
	uint32_t i;

	/* 关闭RX中断 */
	SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
	                  SOC_BIT_USART_CR3_DMAR, SOC_VAL_USART_CR3_DMAR_DIS);
	/* 关闭USART RX DMA请求 */
	SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR3,
	                  SOC_BIT_USART_CR3_DMAR, SOC_VAL_USART_CR3_DMAR_DIS);
	/* 关闭DMA传输 */
	SOC_DMA2_S5.CR = DMA_CR_DIS;
	/* 等待DMA结束 */
	while (_DMA_IsBusy(SOC_DMA2_S5));
	/* 将所有记录置为无效 */
	for (i = 0; i < sizeof(mdmaUsartRx)/sizeof(MDmaUsartRx_t); i++) {
		mdmaUsartRx[i].dma.flag = MDMA_FLAG_DMA;
	}
	/* 使能RX中断，如果有新数据来会触发中断，进而启用DMA接收数据 */
	SOC_BITBAND_WRITE(SOC_BASE_USART1, SOC_OFF_USART_CR1,
	          SOC_BIT_USART_CR1_RXNEIE, SOC_VAL_USART_CR1_RXNEIE_EN);
}
#endif

static uint8_t _buf[128];

void mUsartTest(void)
{
	uint32_t count;

	count = 10;
	while (count--) {
		mBoardDelayMs(200);
		mDebugPuts("hello world!!!");
		mUsartTxString("hello world!!!\r\n");
		mUsartTxString("hello world!!!\r\n");
		mUsartTxString("hello world!!!\r\n");
	}

	while (1) {
		mUsartTxByte(mUsartRxByte());
		count = mUsartRxBytes(_buf, sizeof(_buf));
		mUsartTxBytes(_buf, count);
	}
}
